import cv2 as cv
import copy
import numpy as np
#读入原始图像
filename = r"rice.jpg"
img = cv.imread(filename)
#将彩色图像转化为灰度图像
gray = cv.cvtColor(img,cv.COLOR_BGR2GRAY)
# 大津算法对灰度进行二值化
_, bw = cv.threshold(gray,0,0xff,cv.THRESH_OTSU)
# 用大津算法对灰度图像进行自动阈值化
element = cv.getStructuringElement(cv.MORPH_CROSS,(3,3))
# 数学形态学开运算减少噪声和米粒的粘连
bw = cv.morphologyEx(bw,cv.MORPH_OPEN,element)
seg = copy.deepcopy(bw)   #对阈值化结果进行拷贝
# 检测各个分割后区域的轮廓
bin,cnts,hier = cv.findContours(seg,cv.RETR_EXTERNAL,cv.CHAIN_APPROX_SIMPLE)
#  相关的函数
# D-P法多边形拟合
# approxCurve = cv.approxPolyDP(curve,epsilon,closed[,approxCurve])
# # 计算轮廓线的长度
# retval = cv.arcLength(curve,closed)
# #计算轮廓的面积
# retval = cv.contourArea(contour[,oriented])
# # 计算轮廓包围矩形（水平的）
# retval = cv.boundingRect(array)
# #计算轮廓包围矩形（斜的）
# retval = cv.minAreaRect(points)
# #计算轮廓拟合椭圆
# retval = cv.fitElllipse(points)
count = 0
area_array = np.array([])  # 米粒面积数组
line_array = np.array([])  # 米粒长度数组
for i in range(len(cnts), 0, -1):
    cnt = cnts[i - 1]
    area = cv.contourArea(cnt)  # 米粒面积
    if area < 10:
        continue
    count = count + 1
    area_array = np.append(area_array, area)

    minAreaRect = cv.minAreaRect(cnt) # 精确米粒边界
    x, y, z, p = cv.boxPoints(minAreaRect)  # 得到米粒的4个坐标
    # 计算米粒的长度
    l1 = ((x[0] - y[0]) ** 2 + (x[1] - y[1]) ** 2) ** 0.5 # 计算米粒边界长度
    l2 = ((x[0] - p[0]) ** 2 + (x[1] - p[1]) ** 2) ** 0.5
    if l1 > l2:
        line = l1
    else:
        line = l2
    print("米粒", i, "面积:", area, "长度:", round(line, 2))
    line_array = np.append(line_array, line)
    cv.line(img, tuple(x), tuple(y), (255, 0, 0), 1)
    cv.line(img, tuple(y), tuple(z), (255, 0, 0), 1)
    cv.line(img, tuple(z), tuple(p), (255, 0, 0), 1)
    cv.line(img, tuple(p), tuple(x), (255, 0, 0), 1)
    cv.putText(img, str(count), (x[0], x[1]), cv.FONT_HERSHEY_PLAIN, 0.5, (0, 0xff, 0)) # 标记字体信息
print("------------------------------")
print("米粒总数量: ", len(area_array))
print("------------------------------")
area_all = area_array.sum()
print("总面积:", round(area_all, 3))
average_area = area_all / count
print("平均面积:", round(average_area, 3))
std = area_array.std()
print("面积标准差:", round(std, 3))
area1 = average_area - std * 1.5
area2 = average_area + std * 1.5
print("面积3sigma的取值范围:", round(area1, 3), "--", round(area2, 3))
count1 = 0
for i in area_array:
    if i > area1 and i < area2:
        count1 = count1 + 1
print("米粒面积在3sigma内的数量为:", count1)
print("------------------------------")
line_all = line_array.sum()
print("总长度:", round(line_all, 3))
average_line = line_all / count
print("平均长度:", round(average_line, 3))
std_line = line_array.std()
print("长度标准差:", round(std_line, 3))
line1 = average_line - std_line * 1.5
line2 = average_line + std_line * 1.5
print("长度3sigma的取值范围:", round(line1, 3), "--", round(line2, 3))
count2 = 0
for i in line_array:
    if line2 > i > line1:
        count2 = count2 + 1
print("米粒长度在3sigma内的数量为:", count2)
print("------------------------------")
print("Number of rice grains:",count)
cv.imshow("gray image",gray)
cv.imshow("The original image",img)
cv.imshow("Threshold image",bw)
cv.waitKey()
cv.destroyAllWindows()
